/*
 * XMLGenerator.java
 */

package file;

import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Random;

import exceptions.UPException;

import model.Job;
import model.Park;
import model.PhoneNumber;
import model.PostalAddress;
import model.Role;
import model.User;

/**
 * XMLGenerator class is a program that creates random
 * xml files with which to test the Urban Parks program.
 * 
 * @author Sean Brown
 * @version 1.0
 */
public class XMLGenerator {
	
	/**
	 * The number of users to generate.
	 */
	private static final int NUM_USERS = 60;
	
	/**
	 * The number of jobs to generate.
	 */
	private static final int NUM_JOBS = 25;
	
	/**
	 * An array of generic first names.
	 */
	private static final String[] FNAMES = {"Andrew", "Brian", "Caitlin", "Doug", "Effy", "Frank",
		"Gina", "Hank", "Ida", "Jacob", "Kristen", "Liam", "Mitch", "Natalie", "Olivia", "Percy", 
		"Quinn", "Rita", "Steve", "Tawdy", "Ulysses", "Victoria", "Wayne", "Xena", "Yancy", "Zed"};

	/**
	 * An array of generic last names.
	 */
	private static final String[] LNAMES = {"Apatow", "Brown", "Cerwin", "Daniels", "Elam", "Fredrickson",
		"Gendreau", "Hendrix", "Ipswitch", "Jones", "Killroy", "Landry", "Mitchell", "Nord", "Oliver", 
		"Pierson", "Quaker", "Randall", "Smith", "Tosh", "Underhill", "VanHeussen", "Wade", "Xilo",
		"Young", "Zimmerman"};

	/**
	 * An array of generic email webservers.
	 */
	private static final String[] EMAIL_SERVERS = {"gmail.com", "hotmail.com", "live.com", "yahoo.com", 
		"rocketmail.com"};
	
	/**
	 * An array of city names in WA.
	 */
	private static final String[] CITIES = {"Bellingham", "Everett", "Seattle", "Tacoma", "Puyallup",
		"Olympia", "Aberdeen", "Lacey", "Tumwater", "Tukwilla", "Bellevue", "Spokane", "Walla Walla"};
	
	/**
	 * An array of street names.
	 */
	private static final String[] STREETS = {"1st", "2nd", "3rd", "4th", "5th", "Pacific", "Meridian", 
		"Washington"};
	
	/**
	 * An array of street types.
	 */
	private static final String[] STREET_TYPES = {"Ave", "St", "St Ct", "Ln", "Dr"};
			
	/**
	 * Washington state string.
	 */
	private static final String STATE = "WA";
	
	/**
	 * An array of park names in WA.
	 */
	private static final String[] PARK_NAMES = {"Alta Lake", "Anderson Lake", "Battle Ground Lake", 
		"Bay View", "Beacon Rock", "Belfair", "Birch Bay", "Blake Island", "Blind Island", "Bogachiel",
		"Bridgeport", "Bridle Trails", "Brooks Memorial", "Cama Beach", "Camano Island",
		"Cape Disappointment", "Centennial Trail", "Columbia Hills", "Columbia Plateau Trail",
		"Conconully", "Crawford", "Curlew Lake", "Columbia Park", "Damon Point", "Daroga", "Dash Point", 
		"Deception Pass", "Dosewallips", "Doug's Beach", "Fay", "Federation Forest", "Fields Spring", 
		"Flaming Geyser", "Fort Casey", "Fort Columbia", "Fort Ebey", "Fort Flagler", "Fort Okanogan"}; 
	
	/**
	 * A generic array of regions.
	 */
	private static final String[] REGIONS = {"CENTRAL", "NW", "NE", "SW", "SE"};
	
	/**
	 * An array of all counties in WA.
	 */
	private static final String[] DISTRICTS = {"Adams", "Asotin", "Benton", "Chelan", "Clallam",
		"Clark", "Columbia", "Cowlitz", "Douglas", "Ferry", "Franklin", "Garfield", "Grant",
		"Grays Harbor", "Island", "Jefferson", "King", "Kitsap", "Kittitas", "Klickitat", 
		"Lewis", "Lincoln", "Mason", "Okanogan", "Pacific", "Pend Oreille", "Pierce", "San Juan", 
		"Skagit", "Skamania", "Snohomish", "Spokane", "Stevens", "Thurston", "Wahkiakum", 
		"Walla Walla", "Whatcom", "Whitman", "Yakima"};
	
	/**
	 * An array of generic job descriptions.
	 */
	private static final String[] JOB_NAMES = {"General Cleanup", "Painting", "Construction",
		"Foliage Removal", "Trash Service", "Landscaping", "Trail Clearing"};
	
	/**
	 * The date and time when the program was instantiated.
	 */
	private static final GregorianCalendar CURRENT_DATE = new GregorianCalendar();
	
	/**
	 * The list to store User objects.
	 */
	private final List<User> my_users;
	
	/**
	 * The list to store Park objects.
	 */
	private final List<Park> my_parks;
	
	/**
	 * The list to store Job objects.
	 */
	private final List<Job> my_jobs;
	
	/**
	 * A random number generator.
	 */
	private final Random my_rand;
	
	/**
	 * Instantiates all fields and populates the lists.
	 */
	public XMLGenerator() {
		my_rand = new Random();
		my_users = generateUsers();
		my_parks = generateParks();
		my_jobs = generateJobs();
	}
	
	/**
	 * Main method instantiates an XMLGenerator and saves
	 * the lists to an XML file.
	 * 
	 * @param the_args Not used here.
	 */
	public static void main(String[] the_args) {
		final XMLGenerator xmlgen = new XMLGenerator();
		try {
			xmlgen.start();
		} catch (UPException e) {
			System.err.println(e.getMessage());
		}
	}
	
	/**
	 * Creates an XMLFacade and saves all the lists previously
	 * generated.
	 * @throws UPException 
	 */
	public void start() throws UPException {
		final XMLFacade facade = new XMLFacade();
		facade.saveUsers(my_users);
		facade.saveParks(my_parks);
		facade.saveJobs(my_jobs);
		System.out.println("Files generated successfully!");
	}
	
	/**
	 * Returns a randomly generated list of User objects.
	 * 
	 * @return list of Users.
	 */
	private List<User> generateUsers() {
		final List<User> users = new ArrayList<User>();
		for (int i = 0; i < NUM_USERS; i++) {
			final int x = my_rand.nextInt(FNAMES.length);
			final int y = my_rand.nextInt(LNAMES.length);
			final int z = my_rand.nextInt(EMAIL_SERVERS.length);
			final User user = new User(FNAMES[x], LNAMES[y],
					FNAMES[x] + "_" + LNAMES[y] + "@" + EMAIL_SERVERS[z],
					getRandomPhoneNumber(), Role.values()[i % 3], "",
					getRandomAddress(), false);
			if (!users.contains(user)) {
				users.add(user);
			}
		}
		return users;
	}
	
	/**
	 * Returns a randomly generated PhoneNumber object.
	 * 
	 * @return a PhoneNumber.
	 */
	private PhoneNumber getRandomPhoneNumber() {
		StringBuilder sb = new StringBuilder();
		sb.append(my_rand.nextInt(8) + 2);
		for (int i = 0; i < 9; i++) {
			sb.append(my_rand.nextInt(10));
		}
		return new PhoneNumber(sb.toString());
	}
	
	/**
	 * Returns a randomly generated PostalAddress object.
	 * 
	 * @return a PostalAddress.
	 */
	private PostalAddress getRandomAddress() {
		final int house_no = my_rand.nextInt(100000);
		final String space = " ";
		return new PostalAddress(house_no + space + 
				STREETS[my_rand.nextInt(STREETS.length)] +
				space + STREET_TYPES[my_rand.nextInt(STREET_TYPES.length)],
				CITIES[my_rand.nextInt(CITIES.length)],
				STATE, "9" + my_rand.nextInt(10000));
	}
	
	/**
	 * Returns a randomly generated list of Parks.
	 * 
	 * @return list of Parks.
	 */
	private List<Park> generateParks() {
		final List<Park> parks = new ArrayList<Park>();
		final List<User> managers = new ArrayList<User>();
		for (User u : my_users) {
			if (u.getRole().equals(Role.PARKMANAGER)) {
				managers.add(u);
			}
		}
		for (int i = 0; i < PARK_NAMES.length; i++) {
			final Park p = new Park(PARK_NAMES[i], getRandomAddress(),
					getRandomPhoneNumber(), 
					managers.get(my_rand.nextInt(managers.size())).getID(),
					DISTRICTS[my_rand.nextInt(DISTRICTS.length)],
					REGIONS[my_rand.nextInt(REGIONS.length)]);
			parks.add(p);
		}
		return parks;
	}
	
	/**
	 * Returns a randomly generated list of Jobs.
	 * 
	 * @return a list of Jobs.
	 */
	private List<Job> generateJobs() {
		final List<Job> jobs = new ArrayList<Job>();
		for (int i = 0; i < NUM_JOBS; i++) {
			final GregorianCalendar gc = createDate();
			
			final List<User> volunteers = new ArrayList<User>();
			for (User u : my_users) {
				if (u.getRole().equals(Role.VOLUNTEER)) {
					volunteers.add(u);
				}
			}
			final Park p = my_parks.get(my_rand.nextInt(my_parks.size()));
			final Job job = new Job(JOB_NAMES[my_rand.nextInt(JOB_NAMES.length)], 
					p.getID(),
					gc, createDateAfter(gc), 
					my_rand.nextInt(10), my_rand.nextInt(10), my_rand.nextInt(10));
			for (int j = 0; j < my_rand.nextInt(15); j++) {
				job.addVolunteer(volunteers.get(my_rand.nextInt(volunteers.size())).getID(), 
						my_rand.nextInt(3));
			}
			jobs.add(job);
		}
		return jobs;
	}
	
	/**
	 * Returns a random date/time after the date 
	 * when the program was run.
	 * 
	 * @return A date.
	 */
	private GregorianCalendar createDate() {
		final GregorianCalendar new_date = (GregorianCalendar) CURRENT_DATE.clone();
		new_date.add(GregorianCalendar.HOUR, my_rand.nextInt(12));
		new_date.add(GregorianCalendar.MINUTE, my_rand.nextInt(60));
		new_date.add(GregorianCalendar.DATE, my_rand.nextInt(90));
		return new_date;
	}
	
	/**
	 * Returns a random date/time after the date passed.
	 * 
	 * @assumes the_date is non-null.
	 * @param the_date The date to examine
	 * @return A date within two days after the date passed as a parameter.
	 */
	private GregorianCalendar createDateAfter(final GregorianCalendar the_date) {
		final GregorianCalendar new_end_date = (GregorianCalendar) the_date.clone();
		new_end_date.add(GregorianCalendar.HOUR, my_rand.nextInt(48));
		return new_end_date;
	}
}
